package cn.iocoder.yudao.module.worker.service.reward;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.mp.api.user.MpUserApi;
import cn.iocoder.yudao.module.mp.api.user.dto.UserMpTemplateMsgSendReqDTO;
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendTypeEnum;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.trade.api.config.TradeConfigApi;
import cn.iocoder.yudao.module.trade.api.config.dto.TradeConfigRespDTO;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderCreateReqDTO;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.worker.controller.app.reward.vo.AppWorkerRewardPageReqVO;
import cn.iocoder.yudao.module.worker.controller.app.reward.vo.AppWorkerRewardRespVO;
import cn.iocoder.yudao.module.worker.controller.app.reward.vo.AppWorkerRewardSaveReqVO;
import cn.iocoder.yudao.module.worker.dal.dataobject.clerk.ClerkDO;
import cn.iocoder.yudao.module.worker.dal.dataobject.gift.WorkerGiftDO;
import cn.iocoder.yudao.module.worker.dal.mysql.clerk.ClerkMapper;
import cn.iocoder.yudao.module.worker.dal.mysql.gift.WorkerGiftMapper;
import cn.iocoder.yudao.module.worker.enums.reward.RewardTypeEnum;
import cn.iocoder.yudao.module.worker.service.reward.handler.RewardOrderHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.worker.controller.admin.reward.vo.*;
import cn.iocoder.yudao.module.worker.dal.dataobject.reward.WorkerRewardDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.worker.dal.mysql.reward.WorkerRewardMapper;

import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR;
import static cn.iocoder.yudao.module.worker.enums.ErrorCodeConstants.*;

/**
 * 打赏记录 Service 实现类
 *
 * @author Jerry
 */
@Service
@Validated
@Slf4j
public class WorkerRewardServiceImpl implements WorkerRewardService {

    @Resource
    private WorkerRewardMapper rewardMapper;

    @Resource
    private WorkerGiftMapper workerGiftMapper;

    @Resource
    private ClerkMapper clerkMapper;

    @Resource
    private TradeOrderApi tradeOrderApi;

    @Resource
    private TradeConfigApi tradeConfigApi;

    @Resource
    private PayOrderApi payOrderApi;

    @Resource
    private MemberUserApi memberUserApi;

    @Resource
    public SocialClientApi socialClientApi;

    @Resource
    private MpUserApi mpUserApi;

    @Resource
    private List<RewardOrderHandler> rewardOrderHandlers;

    @Override
    public Long createReward(WorkerRewardSaveReqVO createReqVO) {
        // 插入
        WorkerRewardDO reward = BeanUtils.toBean(createReqVO, WorkerRewardDO.class);
        rewardMapper.insert(reward);
        // 返回
        return reward.getId();
    }

    @Override
    public void updateReward(WorkerRewardSaveReqVO updateReqVO) {
        // 校验存在
        validateRewardExists(updateReqVO.getId());
        // 更新
        WorkerRewardDO updateObj = BeanUtils.toBean(updateReqVO, WorkerRewardDO.class);
        rewardMapper.updateById(updateObj);
    }

    @Override
    public void deleteReward(Long id) {
        // 校验存在
        validateRewardExists(id);
        // 删除
        rewardMapper.deleteById(id);
    }

    private void validateRewardExists(Long id) {
        if (rewardMapper.selectById(id) == null) {
            throw exception(REWARD_NOT_EXISTS);
        }
    }

    @Override
    public WorkerRewardDO getReward(Long id) {
        return rewardMapper.selectById(id);
    }

    @Override
    public PageResult<WorkerRewardDO> getRewardPage(WorkerRewardPageReqVO pageReqVO) {
        return rewardMapper.selectPage(pageReqVO);
    }

    @Override
    public Long createReward(AppWorkerRewardSaveReqVO createReqVO) {
        // 插入
        WorkerRewardDO reward = BeanUtils.toBean(createReqVO, WorkerRewardDO.class);
        rewardMapper.insert(reward);
        // 返回
        return reward.getId();
    }

    @Override
    public void updateReward(AppWorkerRewardSaveReqVO updateReqVO) {
        // 校验存在
        validateRewardExists(updateReqVO.getId());
        // 更新
        WorkerRewardDO updateObj = BeanUtils.toBean(updateReqVO, WorkerRewardDO.class);
        rewardMapper.updateById(updateObj);
    }

    @Override
    public PageResult<WorkerRewardDO> getRewardPage(AppWorkerRewardPageReqVO pageReqVO) {
        return rewardMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AppWorkerRewardRespVO createOrder(Long userId, AppWorkerRewardSaveReqVO createReqVO) {
        MemberUserRespDTO reqDTO = new MemberUserRespDTO();
        reqDTO.setId(userId);
        reqDTO.setWeixin(createReqVO.getWeixin());
        memberUserApi.updateUser(reqDTO);

        WorkerRewardDO rewardDO = BeanUtils.toBean(createReqVO, WorkerRewardDO.class);
        if(createReqVO.getWorkerClerkId() != null){
            ClerkDO clerkDO = clerkMapper.selectById(createReqVO.getWorkerClerkId());
            rewardDO.setWorkerUserId(clerkDO.getUserId());
        }
        if(RewardTypeEnum.isGift(createReqVO.getRewardType())){
            WorkerGiftDO giftDO = workerGiftMapper.selectById(createReqVO.getGiftId());
            rewardDO.setGift(giftDO.getName());
            rewardDO.setPayMoney(rewardDO.getCount() * giftDO.getMoney());
        }else{
            rewardDO.setGift(RewardTypeEnum.REWARD.getName());
        }

        TradeConfigRespDTO configRespDTO = tradeConfigApi.getTradeConfig();
        Integer money = calculatePrice(rewardDO.getPayMoney(), configRespDTO.getBrokerageRewardPercent(), 0);
        rewardDO.setFee(rewardDO.getPayMoney() - money);
        rewardDO.setMoney(money);
        rewardDO.setUserId(userId);
        rewardMapper.insert(rewardDO);

        TradeOrderCreateReqDTO createReqDTO = new TradeOrderCreateReqDTO();
        createReqDTO.setId(rewardDO.getId());
        createReqDTO.setSubject(rewardDO.getGift());
        createReqDTO.setPayPrice(rewardDO.getPayMoney());
        TradeOrderRespDTO respDTO = tradeOrderApi.createReWardOrder(createReqDTO);

        rewardDO.setPayOrderId(respDTO.getPayOrderId());
        rewardMapper.updateById(rewardDO);
        return new AppWorkerRewardRespVO().setId(respDTO.getId()).setPayOrderId(respDTO.getPayOrderId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderPaid(Long id, Long payOrderId) {
        // 校验并获得交易订单（可支付）
        KeyValue<WorkerRewardDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
        WorkerRewardDO order = orderResult.getKey();

        // 更新订单状态为已支付
        order.setStatus(true);
        rewardMapper.updateById(order);

        getSelf().sendOrderMessage(order);

        // 执行 RewardOrderHandler 的后置处理
        rewardOrderHandlers.forEach(handler -> handler.afterPayOrder(order));
    }

    @Async
    public void sendOrderMessage(WorkerRewardDO order) {
        MemberUserRespDTO member = memberUserApi.getUser(order.getUserId());
        mpUserApi.sendTemplateMsg(new UserMpTemplateMsgSendReqDTO()
                .setUserId(order.getWorkerUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
                .setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())
                .setMsgType(MpMessageSendTypeEnum.ORDER_SUCCESS.getType())
                .setPage("pages/reward/list")
                .addMessage("thing11", "有客人打赏了"+order.getCount()+"个:"+order.getGift()) // 消费项目
                .addMessage("thing10", member.getNickname()) // 用户名称
                .addMessage("time16", DateUtil.now())); // 完成时间
    }

    @Override
    public List<WorkerRewardDO> getTopList() {
        return rewardMapper.getTopList();
    }

    @Override
    public List<WorkerRewardDO> getGiftList(Long userId, Long workerClerkId) {
        return rewardMapper.getGiftList(userId, workerClerkId);
    }

    private KeyValue<WorkerRewardDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
        // 校验订单是否存在
        WorkerRewardDO order = rewardMapper.selectById(id);
        // 校验支付订单匹配
        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！order 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }

        // 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在，请进行处理！]", id, payOrderId);
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 校验支付金额一致
        if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayMoney())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
        }
        // 校验支付订单匹配（二次）
        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }
        return new KeyValue<>(order, payOrder);
    }

    /**
     * 计算佣金
     *
     * @param basePrice  佣金基数
     * @param percent    佣金比例
     * @param fixedPrice 固定佣金
     * @return 佣金
     */
    int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) {
        // 1. 优先使用固定佣金
        if (fixedPrice != null && fixedPrice > 0) {
            return ObjectUtil.defaultIfNull(fixedPrice, 0);
        }
        // 2. 根据比例计算佣金
        if (basePrice != null && basePrice > 0 && percent != null && percent > 0) {
            return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent));
        }
        return 0;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private WorkerRewardServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}